using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GameStateManagement;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using GameStateManagementSample.FrameWork.Sprite;
using Microsoft.Xna.Framework.Input.Touch;

namespace GameStateManagementSample.Screens
{
    class PlayScreen: GameScreen
    {
        #region Fields
        ContentManager m_Content;
        Texture2D m_SBack,m_SBackUnder;
        Texture2D[] m_SBG;
        InputAction pauseAction;
        float m_TimeSave;
        float m_TimeDown;
        float m_TimeSaveScore;
        int m_Score, m_ScoreDislay;
        int[,] m_Board;
        int m_Width, m_Height;
        int[,] m_Brick;
        int m_Size,m_Status,m_Type,m_Total;
        int m_X, m_Y;
        int m_Color,m_ColorNext,m_ColorHold;
        int m_BrickNext,m_BrickHold;
        Sprite m_STetris,m_SNumber;
        Random m_Ran;
        bool m_iHold;// iHola = true => allow user hold
        Rectangle m_BTLeft, m_BTRight, m_BTDown, m_BTRotaLeft, m_BTRotaRight,m_BTHold;
        bool iother=false;
        bool[] m_CheckBG;
        int m_OffsetLevel, m_ScoreLevel;
#endregion
#region Initialization
        public PlayScreen()
        {

            TransitionOnTime = TimeSpan.FromSeconds(1.5f);
            TransitionOffTime = TimeSpan.FromSeconds(0.5f);
            pauseAction = new InputAction(
                new Buttons[] { Buttons.Start, Buttons.Back },
                new Keys[] { Keys.Escape },
                true);
        }
        public override void Activate(bool instancePreserved)
        {
            if (!instancePreserved)
            {
                if (m_Content == null)
                {
                    m_Content = new ContentManager(ScreenManager.Game.Services, "Content");
                    ScreenManager.Game.ResetElapsedTime();
                    m_SBack = m_Content.Load<Texture2D>(@"Image/bggame");

                    m_CheckBG = new bool[9];
                    m_SBG = new Texture2D[9];
                    for (int i = 0; i < 9; i ++ )
                    {
                        m_SBG[i] = m_Content.Load<Texture2D>(@"Image/bggame"+(i+1).ToString());
                    }
                    
                    m_STetris = new SpriteEX(ScreenManager.Game, @"Image/tetris", 28, 28, 7, Color.White);
                    m_SNumber   = new SpriteEX(ScreenManager.Game,@"Image/NoRed1",37,39,10,Color.White);

                    

                    m_BTLeft        = new Rectangle(  0, 540, 100, 100);
                    m_BTRight       = new Rectangle(380, 540, 100, 100);
                    m_BTRotaLeft    = new Rectangle(  0, 374, 100, 100);
                    m_BTRotaRight   = new Rectangle(380, 374, 100, 100);
                    m_BTDown        = new Rectangle(100, 730, 280,  70);
                    m_BTHold        = new Rectangle(0, 170, 100, 160);

                    

                    m_Width = 10;
                    m_Height = 21;
                    m_Board = new int[m_Width, m_Height];

                    for (int i = 0; i < m_Width; i++)
                    {
                        for (int j = 0; j < m_Height; j++)
                        {
                            m_Board[i, j] = 0;
                        }
                    }
                    m_Ran = new Random();

                    NewBrick(m_Ran.Next(7),m_Ran.Next(7));
                    
                    m_BrickNext = m_Ran.Next(7);
                    m_ColorNext = m_Ran.Next(7);
                    m_BrickHold = -1;
                    m_iHold = true;

                    m_Score = 0;
                    m_ScoreDislay = 0;
                    m_TimeSaveScore = 0;
                    m_TimeDown = 1.0f;
                    m_TimeSave = 0.0f;
                    m_ScoreLevel = 0;
                    m_OffsetLevel = 100;
                    ChangeBG();
                }
            }
        }
        public override void Deactivate()
        {
            base.Deactivate();
        }
        public override void Unload()
        {
            base.Unload();
        }
#endregion
#region Update and Draw
        protected virtual void OnCancel() 
        {
            
        }
        public override void Update(Microsoft.Xna.Framework.GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, false);
            iother = otherScreenHasFocus;
            if (!otherScreenHasFocus)
            {
                UpdateTimeDown(gameTime);
                UpdateScoreDisplay(gameTime);
            }
           
        }
        public override void HandleInput(Microsoft.Xna.Framework.GameTime gameTime, InputState input)
        {
            if (input == null)
            {
                throw new ArgumentNullException("input");
            }
            PlayerIndex player;
            if (pauseAction.Evaluate(input,ControllingPlayer,out player))
            {
                ScreenManager.AddScreen(new PauseScreen(), PlayerIndex.One);
            }
            TouchCollection touches = input.TouchState;
            
            foreach (TouchLocation touch in touches)
            {
                if (touch.State == TouchLocationState.Pressed)
                {
                    if (m_BTLeft.Contains((int)touch.Position.X,(int)touch.Position.Y))
                    {
                        MoveBrick(m_Brick,m_X,m_Y,m_Size,m_Board,m_Width,m_Height,-1,out m_X,out m_Y);
                    }
                    if (m_BTRight.Contains((int)touch.Position.X, (int)touch.Position.Y))
                    {
                        MoveBrick(m_Brick, m_X, m_Y, m_Size, m_Board, m_Width, m_Height, 1, out m_X, out m_Y);
                    }
                    if (m_BTDown.Contains((int)touch.Position.X, (int)touch.Position.Y))
                    {
                         DownShort(m_Brick,m_X,m_Y,m_Size,m_Board,m_Width,m_Height);             
                    }
                    if (m_BTRotaLeft.Contains((int)touch.Position.X, (int)touch.Position.Y))
                    {
                        RotaionBrick(m_Brick,m_X,m_Y,m_Size,m_Type,m_Status,m_Total,m_Board,m_Width,m_Height,1);
                    }
                    if (m_BTRotaRight.Contains((int)touch.Position.X, (int)touch.Position.Y))
                    {
                        RotaionBrick(m_Brick, m_X, m_Y, m_Size, m_Type, m_Status, m_Total, m_Board, m_Width, m_Height,-1);
                    }
                    if (m_BTHold.Contains((int)touch.Position.X, (int)touch.Position.Y))
                    {
                        TouchHold();
                    }
                }
            }
        }
        public override void Draw(Microsoft.Xna.Framework.GameTime gameTime)
        {
            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;

            spriteBatch.Begin();
            spriteBatch.Draw(m_SBackUnder, Vector2.Zero, Color.White * TransitionAlpha);
            spriteBatch.Draw(m_SBack, Vector2.Zero, Color.White * TransitionAlpha);
            DrawBoard(spriteBatch, m_STetris, m_Board, m_Width, m_Height);
            DrawBrick(spriteBatch, m_STetris, m_Brick, m_X, m_Y, m_Size, 2);
            DrawNextBrick(spriteBatch, m_STetris, m_BrickNext);
            DrawHoldBrick(spriteBatch, m_STetris, m_BrickHold);
            DrawBrickFuture(spriteBatch, m_STetris, m_Brick, m_X, m_Y, m_Size, m_Board, m_Width, m_Height);
            DrawScore(spriteBatch, m_SNumber, m_ScoreDislay);
            GameStateManagementGame game = (GameStateManagementGame)ScreenManager.Game;
            DrawBestScore(spriteBatch, m_SNumber, game.m_BestScore);
            spriteBatch.End();
            if (iother)
            {
                float alpha = MathHelper.Lerp(1f - TransitionAlpha, 1f, TransitionAlpha / 2);

                ScreenManager.FadeBackBufferToBlack(alpha);
            }
        }
#endregion
#region Tetris
        void DrawBrick (SpriteBatch _spriteBatch,Sprite _STetris,int[,] _Brick,int _X,int _Y,int _Size,int _Color)
        {
            for (int i = 0; i < _Size; i++)
            {
                for (int j = 0; j < _Size; j++)
                {
                    if (_Brick[i, j] != 0)
                    {
                        //_STetris.Draw(_spriteBatch, 100 + (_X + i) * 28, 142 + (_Y + j) * 28, 1);
                        _STetris.Draw(_spriteBatch,
                            100 + (_X + i) * 28, 142 + (_Y + j) * 28, m_Color, 1, Color.White, 0.0f,
                            SpriteEffects.None, 1.0f,1.0f);
                    }
                }
            }
        }
        void DrawNextBrick(SpriteBatch _spriteBatch,Sprite _STetris,int _BrickNext)
        {
            if (_BrickNext <0 )
            {
                return;
            }
            int[,] brick = Tetris.Instance.Data(_BrickNext,0);
            int Size = Tetris.Instance.Size(_BrickNext);
            for (int i = 0; i < Size; i++)
            {
                for (int j = 0; j < Size; j++)
                {
                    if (brick[i, j] != 0)
                    {
                        _STetris.Draw(_spriteBatch,
                            385 + (4 - Size) * 10 + i * 20, 256 + j * 20, m_ColorNext, 1, Color.White, 0.0f,
                            SpriteEffects.None, 5.0f / 7.0f, 5.0f / 7.0f);
                    }
                }
            }
        }
        void DrawHoldBrick(SpriteBatch _spriteBatch, Sprite _STetris, int _BrickHold)
        {
            if (_BrickHold < 0)
            {
                return;
            }
            int[,] brick = Tetris.Instance.Data(_BrickHold, 0);
            int Size = Tetris.Instance.Size(_BrickHold);
            for (int i = 0; i < Size; i++)
            {
                for (int j = 0; j < Size; j++)
                {
                    if (brick[i, j] != 0)
                    {
                        _STetris.Draw(_spriteBatch,
                            5 + (4 - Size) * 10 + i * 20, 256 + j * 20, m_ColorHold, 1, Color.White, 0.0f,
                            SpriteEffects.None, 5.0f / 7.0f, 5.0f / 7.0f);
                    }
                }
            }
        }
        void DrawBoard(SpriteBatch _spriteBatch, Sprite _STetris, int[,] _Board, int _Width, int _Height)
        {
            for (int i = 0; i < _Width; i++)
            {
                for (int j = 0; j < _Height; j++)
                {
                    if (_Board[i, j] != 0)
                    {
                        _STetris.Draw(_spriteBatch, 100 + i * 28, 142 + j * 28, _Board[i, j] - 1);
                    }
                }
            }
        }

        private void DrawBrickFuture(SpriteBatch _spriteBatch, Sprite _STetris, int[,] _Brick,int _X,int _Y,int _Size, int[,] _Board,int _Width,int _Height)
        {
            int OldY = _Y;
            int OldX = _X;
            while (MoveBrick(_Brick, OldX, OldY, _Size, _Board, _Width, _Height, 0, out OldX, out OldY)) ;
            for (int i = 0; i < _Size; i++)
            {
                for (int j = 0; j < _Size; j++)
                {
                    if (_Brick[i, j] != 0)
                    {
                        _STetris.Draw(_spriteBatch, 100 + (OldX + i) * 28, 142 + (OldY + j) * 28, m_Color,
                            1.0f, new Color(1,1,1,0.4f), 0.0f, SpriteEffects.None, 1.0f, 1.0f);
                        _STetris.Draw(_spriteBatch, 100 + (OldX + i) * 28, 142 + (OldY + j) * 28, m_Color,
                            1.0f, Color.White*0.5f, 0.0f, SpriteEffects.None, 1.0f, 1.0f);
                    }
                }
            }
           
        }
        /* Check collision brick and board
         * result False : have collision
         * result True  : no collisionS
         */
        public bool CheckBrickVSBoard(int[,] _Brick,int _X,int _Y,int _Size,int[,] _Board,int _Width,int _Height)
        {
            for (int i = 0; i < _Size; i++)
            {
                for (int j = 0; j < _Size; j++)
                {
                    if (_Brick[i, j] != 0)
                    {
                        if ((i + _X < 0) ||
                            (i + _X >= _Width)) return false;
                        if ((j + _Y < 0) ||
                            (j + _Y >= _Height)) return false;
                        if (_Board[i + _X, j + _Y] != 0)
                            return false;
                    }
                }
            }
            return true;
        }
        /* Move brick
         * Direction < 0 : move Left
         * Direction > 0 : move Right
         * Direction = 0 : move down
         * Result   = true move complete
         *          = false not complete
         */
        public bool MoveBrick(int[,] _Brick, int _X, int _Y, int _Size, int[,] _Board, int _Width, int _Height,
            int _Direction,out int outX,out int outY)
        {
            int x, y;
            x = _X;
            y = _Y;
            outX = _X;
            outY = _Y;
            if (_Direction == 0)
            {
                y = _Y + 1;
            }
            else
            {
                if (_Direction > 0)
                {
                    x = _X + 1;
                }
                else
                {
                    x=  _X - 1;
                }
            }
            if (CheckBrickVSBoard(_Brick,x,y,_Size, _Board,_Width,_Height))
            {
                outX = x;
                outY = y;
                return true;
            }
            else
            {              
                return false;
            }
        }
       
        public bool RotaionBrick(int[,] _Brick, int _X, int _Y, int _Size,int _Type,int _Status,int _Total, int[,] _Board, int _Width, int _Height,int _Direction)
        {
            if (_Direction > 0)
            {
                if (++_Status >= _Total) _Status = 0;
            }
            else
            {
                if (--_Status < 0) _Status = _Total - 1;
            }
            _Brick = Tetris.Instance.Data(_Type,_Status);
            
            if (CheckBrickVSBoard(_Brick,_X,_Y,_Size, _Board,_Width,_Height))
            {
                m_Brick = Tetris.Instance.Data(_Type,_Status);
                m_Status = _Status;
                return true;
            }
            else
            {              
                return false;
            }            
        }
        bool DownBrick (int[,] _Brick, int _X, int _Y, int _Size, int[,] _Board, int _Width, int _Height)
        {
            if (MoveBrick(_Brick, _X, _Y, _Size, _Board, _Width, _Height, 0, out m_X, out m_Y) == false)
            {
                AddBrick(_Brick,_X,_Y,_Size);
                if (CheckOver())
                {
                    ScreenManager.AddScreen(new OverScreen(m_Score), null);
                }

                NewBrick(m_BrickNext,m_ColorNext);
               
                m_BrickNext= m_Ran.Next(7);
                m_ColorNext = m_Ran.Next(7); 
                m_iHold = true;
                m_TimeSave = 0;

                int t = ClearBoard(_Board,_Width,_Height);
                int score = 0;
                score = t * 5;  
                if (t>2)
                {
                    score += (t - 2) * 5;
                }
                m_Score += score;
                GameStateManagementGame game = (GameStateManagementGame)ScreenManager.Game;
                if (game.m_BestScore<m_Score)
                {
                    game.m_BestScore = m_Score;
                }

                UpdateLevel(score);
                return true;
            }
            return false;
        }
        void DownShort(int[,] _Brick, int _X, int _Y, int _Size, int[,] _Board, int _Width, int _Height)
        {
            int OldY = _Y;
            int OldX = _X;
            while (MoveBrick(_Brick, OldX, OldY, _Size, _Board, _Width, _Height, 0, out OldX, out OldY)) ;
            if (m_Y!= OldY)
            {
                m_TimeSave = 0;
            }
            m_X = OldX;
            m_Y = OldY;
        }
        void AddBrick(int[,] _Brick, int _X, int _Y, int _Size)
        {
            for (int i = 0; i < _Size; i++)
            {
                for (int j = 0; j < _Size; j++)
                {
                    if (_Brick[i, j] != 0)
                    {
                        if ((i + _X < 0) ||
                            (i + _X >= m_Width)) continue;
                        if ((j + _Y < 0) ||
                            (j + _Y >= m_Height)) continue;
                        m_Board[i + _X, j + _Y] = m_Color + 1;

                    }
                }
            }
        }
        void TouchHold()
        {
            if (m_iHold == false)
            {
                return;
            }
            m_iHold = false;
            if (m_BrickHold < 0)
            {
                m_BrickHold = m_Type;
                m_ColorHold = m_Color;
                NewBrick(m_BrickNext,m_ColorNext);

                m_BrickNext = m_Ran.Next(7);
                m_ColorNext = m_Ran.Next(7);
            }
            else
            {
                int _Type = m_Type;
                int _Color = m_Color;
                NewBrick(m_BrickHold,m_ColorHold);

                m_BrickHold = _Type;
                m_ColorHold = _Color; 
            }
        }
        void NewBrick(int _Type,int _Color)
        {
            m_Brick = Tetris.Instance.Data(_Type, 0);
            m_Total = Tetris.Instance.TotalStatus(_Type);
            m_Size = Tetris.Instance.Size(_Type);
            m_X = 4;
            m_Y = 0;
            m_Type = _Type;
            m_Status = 0;
            m_Color = _Color;
        }
        void UpdateScoreDisplay(GameTime _gameTime)
        {
            if (m_ScoreDislay < m_Score)
            {
                m_TimeSaveScore += (float)_gameTime.ElapsedGameTime.TotalSeconds;
                if (m_TimeSaveScore > 0.001f)
                {
                    m_TimeSaveScore -= 0.001f;
                    m_ScoreDislay++;
                }
            }
        }
        void UpdateTimeDown(GameTime _gameTime)
        {
            m_TimeSave += (float)_gameTime.ElapsedGameTime.TotalSeconds;
            if (m_TimeSave > m_TimeDown)
            {
                m_TimeSave -= m_TimeDown;                
                DownBrick(m_Brick, m_X, m_Y, m_Size, m_Board, m_Width, m_Height);                
            }
        }
        private int ClearBoard(int[,] _Board,int _Width,int Height)
        {
            int result = 0;
            for (int j = m_Height - 1; j >= 1; j--)
            {
                bool flag = true;
                for (int i = 0; i < m_Width; i++)
                {
                    if (_Board[i, j] == 0)
                    {
                        flag = false;
                    }
                }
                if (flag == true)
                {
                    result++;
                    for (int f = j; f >= 1; f--)
                    {
                        for (int i = 0; i < _Width; i++)
                        {
                            _Board[i, f] = m_Board[i, f - 1];
                        }
                    }
                    j++;
                }
            }

            return result;
        }
        private void DrawScore(SpriteBatch _SpriteBatch, Sprite _SNumber, int _Score)
        {
            int Count = -37;
            int Num = _Score;
            while (Num / 10 > 0)
            {
                Count += 37;
                Num = Num / 10;
            }
            Num = _Score;
            int Offset = 0;
            _SNumber.Draw(_SpriteBatch, 240 + Count / 2 - Offset, 70, Num % 10);
            Offset += 37;
            while (Num / 10 > 0)
            {
                Num = Num / 10;
                _SNumber.Draw(_SpriteBatch, 240 + Count / 2 - Offset, 70, Num % 10);
                Offset += 37;
            }
        }
        private void DrawBestScore(SpriteBatch _SpriteBatch, Sprite _SNumber, int _Score)
        {
            int Count = -37;
            int Num = _Score;
            while (Num / 10 > 0)
            {
                Count += 37;
                Num = Num / 10;
            }
            Num = _Score;
            int Offset = 0;
            _SNumber.Draw(_SpriteBatch, 400 + Count / 2 - Offset, 20, Num % 10);
            Offset += 37;
            while (Num / 10 > 0)
            {
                Num = Num / 10;
                _SNumber.Draw(_SpriteBatch, 400 + Count / 2 - Offset, 20, Num % 10);
                Offset += 37;
            }
        }
        void ChangeBG()
        {
            int c=0;
            foreach (bool b in m_CheckBG)
            {
                if (!b)
                {
                    c++;
                }
            }
            int n;
            if (c <= 0)
            {
               n = m_Ran.Next(9);
               for (int i = 0; i < 9; i++)
               {
                   if (i==n)
                   {
                       m_SBackUnder = m_SBG[i];
                   }
                   else
                   {
                        m_CheckBG[i] = true;
                   }
                   
               }
            }
            else
            {
                n = m_Ran.Next(c);
                for (int i = 0;i <9;i++)
                {
                    if (!m_CheckBG[i])
                    {
                        if (n == 0)
                        {
                            m_CheckBG[i] = true;
                            m_SBackUnder = m_SBG[i];
                            break;
                        }
                        else
                        {
                            n--;
                        }
                    }
                }
            }
            foreach (bool b in m_CheckBG)
            {
                if (!b)
                {
                    c--;
                }
            }
        }
        void UpdateLevel(int _Score)
        {
            m_ScoreLevel += _Score;
            if (m_ScoreLevel>=m_OffsetLevel)
            {
                m_ScoreLevel -= m_OffsetLevel;
                m_OffsetLevel += 10;
                ChangeBG();
                m_TimeDown -= (m_TimeDown * 0.1f);
            }
        }
        bool CheckOver()
        {
            for (int i = 0; i < m_Width;i++ )
            {
                if (m_Board[i,0]!=0)
                {
                    return true;
                }
            }
            return false;
        }
#endregion
    }
}
